﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using NUnit.Framework;

namespace MemcachedClientTests
{
    [TestFixture]
    public class UDPSocketTest
    {
        private byte[] CreateHeader(short requestID, short sequenceNumber, short numberOfDataGrams)
        {
            byte[] requestIDs = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(requestID));
            byte[] sequenceNumbers = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(sequenceNumber));

            byte[] numberOfDataGramss = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(numberOfDataGrams));

            byte[] reserved = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)0));
            using (MemoryStream ms = new MemoryStream())
            {
                ms.Write(requestIDs, 0, requestIDs.Length);
                ms.Write(sequenceNumbers, 0, sequenceNumbers.Length);
                ms.Write(numberOfDataGramss, 0, numberOfDataGramss.Length);
                ms.Write(reserved, 0, reserved.Length);
                return ms.ToArray();
            }
        }


        [Test]
        public void UDPSendReceive()
        {

            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            AutoResetEvent evt = new AutoResetEvent(false);
            byte[] header = Encoding.ASCII.GetBytes("get test\r\n");
            byte[] udpHeader = CreateHeader(2, 0, 1);
            byte[] package;

            using (MemoryStream ms = new MemoryStream())
            {
                ms.Write(udpHeader, 0, udpHeader.Length);
                ms.Write(header, 0, header.Length);
                package = ms.ToArray();
            }
            s.BeginConnect(
                "127.0.0.1",
                11212,
                ar =>
                {
                    AutoResetEvent evtInner = (AutoResetEvent)ar.AsyncState;

                    try
                    {
                        s.EndConnect(ar);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                    }
                    finally
                    {
                        evtInner.Set();
                    }
                }, evt);
            evt.WaitOne();

            SocketError err = SocketError.Success;

            s.BeginSend(package, 0, package.Length, SocketFlags.None, out err, ar =>
            {
                if (err != SocketError.Success)
                {
                    return;
                }

                AutoResetEvent evtInner = (AutoResetEvent)ar.AsyncState;
                try
                {
                    s.EndSend(ar, out err);
                    Console.WriteLine(err);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
                finally
                {
                    evtInner.Set();
                }
            }, evt);

            evt.WaitOne();
            byte[] receiveBuffer = new byte[1000];
            int numberOfBytesReceived = 0;
            s.BeginReceive(
            receiveBuffer,
            0,
            1000, SocketFlags.None, ar =>
            {
                AutoResetEvent evtInner = (AutoResetEvent)ar.AsyncState;
                try
                {
                    numberOfBytesReceived = s.EndReceive(ar, out err);
                    Console.WriteLine(err);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
                finally
                {
                    evtInner.Set();
                }
            }, evt);
            evt.WaitOne();
            if (numberOfBytesReceived > 0)
            {
                Console.WriteLine("Something received");

                // Frame header is 8 bytes
                // 0-1 Request ID
                // 2-3 Sequence number
                // 4-5 Total number of datagrams in this message
                // 6-7 Reserved for future use; must be 0
                //
                //
                int bytesResponse = numberOfBytesReceived - 8;
                Console.WriteLine(Encoding.ASCII.GetString(receiveBuffer, 8, bytesResponse));
            }
        }
    }
}
